/**
 * 
 */
package com.jdy.haoduoaiteacher.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.jdy.haoduoaiteacher.intface.OnLightnessChangedListener;
import com.jdy.haoduoaiteacher.util.Tools;
import com.ycsj.goldmedalnewconcept.R;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author zp 2016-11-14 下午2:41:20
 */
public class LightnessController extends View {

  protected int WIDTH;

  protected int HEIGHT;

  private final Paint mPaint;

  private int defaultStrokeColor = Color.argb(255, 152, 152, 152);

  private int progressStrokeColor = Color.argb(255, 255, 255, 255);

  private final float strokeWidth = 5;

  private Path mContourPath;

  private ControllerButton mCBtn;

  private PointF REF_CENTER_POINT;

  float ANGLE_RANGE;

  private float START_DEGREE = 25;

  private float END_DEGREE = 70;

  private float INIT_DEGREE = 40;

  private float RADIUS_1 = 540;

  private float DIAMETER = 30;

  private float CONTROLLER_RADIUS = 35;

  private final int SAMPLES = 20;

  private Region mActiveRegion;

  private float ratio = 1;

  private OnLightnessChangedListener mOnLightnessChangedListener;

  private final static String TAG = "LightnessController";

  /**
   * @param context
   * @param attrs
   * @param defStyleAttr
   */
  public LightnessController(Context context, AttributeSet attrs,
      int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    // TODO Auto-generated constructor stub
    mPaint = new Paint();

    mPaint.setAntiAlias(true);

    mPaint.setStyle(Style.FILL_AND_STROKE);

    mPaint.setStrokeWidth(strokeWidth);

    mPaint.setColor(defaultStrokeColor);

    this.ratio = Tools.getPixelRatio(context);

    RADIUS_1 *= this.ratio;

    TypedArray typeArray = context.obtainStyledAttributes(attrs,
        R.styleable.LightnessController);

    CONTROLLER_RADIUS = typeArray.getFloat(
        R.styleable.LightnessController_lc_circle_radius, CONTROLLER_RADIUS);

    DIAMETER = typeArray.getFloat(R.styleable.LightnessController_lc_thickness,
        DIAMETER);

    progressStrokeColor = typeArray.getColor(
        R.styleable.LightnessController_lc_hightlight_color,
        progressStrokeColor);

    defaultStrokeColor = typeArray
        .getColor(R.styleable.LightnessController_lc_background_color,
            defaultStrokeColor);

    START_DEGREE = typeArray.getFloat(
        R.styleable.LightnessController_lc_start_degree, START_DEGREE);

    END_DEGREE = typeArray.getFloat(
        R.styleable.LightnessController_lc_end_degree, END_DEGREE);

    INIT_DEGREE = typeArray.getFloat(
        R.styleable.LightnessController_lc_init_degree, INIT_DEGREE);

    typeArray.recycle();

    DIAMETER *= this.ratio;

    CONTROLLER_RADIUS = this.ratio * 35;

    if (isInEditMode())
      return;

  }

  private void initParameters() {

    REF_CENTER_POINT = new PointF(WIDTH / 2, HEIGHT / 2);

    mContourPath = getContourPoints(Math.toRadians(START_DEGREE),
        Math.toRadians(END_DEGREE), DIAMETER);

    PointF p3 = getPointByAngle(REF_CENTER_POINT, RADIUS_1,
        Math.toRadians(END_DEGREE));

    PointF p4 = getPointByAngle(REF_CENTER_POINT, RADIUS_1 - DIAMETER,
        Math.toRadians(END_DEGREE));

    PointF p34 = new PointF((p3.x + p4.x) / 2, (p3.y + p4.y) / 2);

    mCBtn = new ControllerButton(p34, CONTROLLER_RADIUS);

    RectF rectF = new RectF(0, 0, WIDTH, HEIGHT);

    mActiveRegion = new Region();

    mActiveRegion.setPath(mContourPath, new Region((int) rectF.left,
        (int) rectF.top, (int) rectF.right, (int) rectF.bottom));

    mContourPath.computeBounds(rectF, true);
  }

  /**
   * @param context
   * @param attrs
   */
  public LightnessController(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    // TODO Auto-generated constructor stub
  }

  /**
   * @param context
   */
  public LightnessController(Context context) {
    this(context, null);
    // TODO Auto-generated constructor stub
  }

  @Override
  protected void onSizeChanged(int w, int h, int oldW, int oldH) {
    WIDTH = w;

    HEIGHT = h;

    initParameters();

    // mPaint.setShader(new RadialGradient(WIDTH / 2, HEIGHT / 2, WIDTH / 2,
    // Color.TRANSPARENT, Color.BLACK, TileMode.MIRROR));

  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int desiredWidth = (int) (1200 * this.ratio);

    int desiredHeight = (int) (1200 * this.ratio);

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);

    int widthSize = MeasureSpec.getSize(widthMeasureSpec);

    int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int width;

    int height;

    // Measure Width
    if (widthMode == MeasureSpec.EXACTLY) {
      // Must be this size
      width = widthSize;
    } else if (widthMode == MeasureSpec.AT_MOST) {
      // Can't be bigger than...
      width = Math.min(desiredWidth, widthSize);
    } else {
      // Be whatever you want
      width = desiredWidth;
    }

    // Measure Height
    if (heightMode == MeasureSpec.EXACTLY) {
      // Must be this size
      height = heightSize;
    } else if (heightMode == MeasureSpec.AT_MOST) {
      // Can't be bigger than...
      height = Math.min(desiredHeight, heightSize);
    } else {
      // Be whatever you want
      height = desiredHeight;
    }

    // MUST CALL THIS
    setMeasuredDimension(width, height);
  }

  public void setOnLightnessChangedListener(OnLightnessChangedListener listener) {
    this.mOnLightnessChangedListener = listener;
  }

  private Path getContourPoints(double start, double end, float thickness) {

    float r2 = RADIUS_1 - DIAMETER;

    PointF p1 = getPointByAngle(REF_CENTER_POINT, r2, start);

    PointF p2 = getPointByAngle(REF_CENTER_POINT, RADIUS_1, start);

    PointF p12 = new PointF((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);

    PointF p3 = getPointByAngle(REF_CENTER_POINT, RADIUS_1, end);

    PointF p4 = getPointByAngle(REF_CENTER_POINT, r2, end);

    PointF p34 = new PointF((p3.x + p4.x) / 2, (p3.y + p4.y) / 2);

    List<PointF> contourList = new ArrayList<PointF>();

    contourList.addAll(Arrays.asList(getPointsByAngleRange(REF_CENTER_POINT,
        RADIUS_1, start, end, SAMPLES)));

    contourList.addAll(Arrays.asList(getPointsByAngleRange(p34, DIAMETER / 2,
        end, end + Math.PI, SAMPLES)));

    contourList.addAll(Arrays.asList(getPointsByAngleRange(REF_CENTER_POINT,
        r2, end, start, SAMPLES)));

    contourList.addAll(Arrays.asList(getPointsByAngleRange(p12, DIAMETER / 2,
        start + Math.PI, start + Math.PI * 2, SAMPLES)));

    Path path1 = new Path();

    for (int i = 0; i < contourList.size(); i++) {
      if (i == 0) {
        path1.moveTo(contourList.get(i).x, contourList.get(i).y);
      } else {
        path1.lineTo(contourList.get(i).x, contourList.get(i).y);
      }
    }

    path1.close();

    return path1;
  }

  private PointF getPointByAngle(PointF center, float radius, double rotateAngle) {
    PointF p = new PointF();
    p.x = center.x + radius * (float) Math.cos(rotateAngle);
    p.y = center.y + radius * (float) Math.sin(rotateAngle);
    return p;
  }

  private PointF[] getPointsByAngleRange(PointF center, float radius,
      double start, double end, int samples) {
    double range = end - start;
    double step = range / samples;
    List<PointF> points = new ArrayList<PointF>();
    double c = start;
    if (range > 0) {
      for (; c <= end; c += step) {
        PointF n = getPointByAngle(center, radius, c);
        points.add(n);
      }
    } else {
      for (; c >= end; c += step) {
        PointF n = getPointByAngle(center, radius, c);
        points.add(n);
      }
    }
    return points.toArray(new PointF[points.size()]);
  }

  // public static float angleBetween2Lines(PointF A1, PointF A2, PointF B1,
  // PointF B2) {
  // float angle1 = (float) Math.atan2(A2.y - A1.y, A1.x - A2.x);
  // float angle2 = (float) Math.atan2(B2.y - B1.y, B1.x - B2.x);
  // float calculatedAngle = (float) Math.toDegrees(angle1 - angle2);
  // if (calculatedAngle < 0)
  // calculatedAngle += 360;
  // return calculatedAngle;
  // }

  private class ControllerButton {

    public float Radius;

    public PointF Position;

    public final int BtnColor = Color.argb(255, 255, 255, 255);

    private final int BackColor = Color.rgb(200, 200, 200);

    private Path mProgressPath;

    private double StartRadians = Math.toRadians(INIT_DEGREE);

    private float AddtionalSensibleRadius = 5;

    public ControllerButton(PointF pos, float r) {
      this.Position = pos;

      this.Radius = r;

      this.AddtionalSensibleRadius = r;

      mProgressPath = getContourPoints(StartRadians,
          Math.toRadians(END_DEGREE), DIAMETER);

      this.Position = getPointByAngle(REF_CENTER_POINT,
          RADIUS_1 - DIAMETER / 2, StartRadians);
    }

    public void draw(Canvas canvas) {

      mPaint.setColor(progressStrokeColor);

      canvas.drawPath(mProgressPath, mPaint);

      Paint paint = new Paint();

      paint.setFlags(Paint.ANTI_ALIAS_FLAG);

      paint.setStyle(Style.STROKE);

      paint.setColor(BackColor);

      canvas.drawCircle(Position.x, Position.y, Radius + 1, paint);

      paint.setColor(BtnColor);

      paint.setStyle(Style.FILL);

      canvas.drawCircle(Position.x, Position.y, Radius, paint);

    }

    /**
     * @param xTouch
     * @param yTouch
     */
    public void setPosition(int x, int y) {
      // TODO Auto-generated method stub

      double tx = x - REF_CENTER_POINT.x, ty = y - REF_CENTER_POINT.y;

      double t_length = Math.sqrt(tx * tx + ty * ty);

      StartRadians = Math.PI / 2 - Math.acos(ty / t_length);

      if (StartRadians < Math.toRadians(START_DEGREE))
        StartRadians = Math.toRadians(START_DEGREE + 1);
      else if (StartRadians > Math.toRadians(END_DEGREE))
        StartRadians = Math.toRadians(END_DEGREE - 1);

      if (mOnLightnessChangedListener != null)
        mOnLightnessChangedListener.onLightnessChanged(START_DEGREE + 1,
            END_DEGREE - 1, false, Math.toDegrees(StartRadians));

      this.Position.x = (float) (REF_CENTER_POINT.x + Math.cos(StartRadians)
          * RADIUS_1 - DIAMETER / 2);

      this.Position.y = (float) (REF_CENTER_POINT.y + Math.sin(StartRadians)
          * RADIUS_1 - DIAMETER / 2);

      mProgressPath = getContourPoints(StartRadians,
          Math.toRadians(END_DEGREE), DIAMETER);

    }

    /**
     * @param xTouch
     * @param yTouch
     * @return
     */
    public boolean contains(int x, int y) {
      // TODO Auto-generated method stub
      // return TouchArea.contains(xTouch, xTouch);
      if (x >= this.Position.x - Radius - AddtionalSensibleRadius
          && x <= this.Position.x + Radius + AddtionalSensibleRadius
          && y >= this.Position.y - Radius - AddtionalSensibleRadius
          && y <= this.Position.y + Radius + AddtionalSensibleRadius) {

        return true;
      } else
        return false;
    }
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    mPaint.setColor(defaultStrokeColor);

    canvas.drawPath(mContourPath, mPaint);

    mCBtn.draw(canvas);
  }

  private boolean mSelected = false;

  int xTouch;

  int yTouch;

  @Override
  public boolean onTouchEvent(final MotionEvent event) {
    boolean handled = false;

    // get touch event coordinates and make transparent circle from it
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:

      xTouch = (int) event.getX(0);
      yTouch = (int) event.getY(0);

      this.mSelected = mCBtn.contains(xTouch, yTouch);
      invalidate();
      handled = true;
      break;

    case MotionEvent.ACTION_MOVE:

      Log.w(TAG, "Move");
      // xTouch = (int) event.getX(0);
      // yTouch = (int) event.getY(0);

      if (this.mSelected) {
        this.mCBtn.setPosition((int) event.getX(0), (int) event.getY(0));
      }
      invalidate();
      handled = true;
      break;

    case MotionEvent.ACTION_UP:
      Log.w(TAG, "Motion Up");
      xTouch = (int) event.getX(0);
      yTouch = (int) event.getY(0);
      // this.mOptionButton.setDiscretePosition(xTouch, yTouch,
      // mOptionAreaList);
      this.mSelected = false;
      invalidate();
      handled = true;
      break;

    case MotionEvent.ACTION_CANCEL:
      Log.w(TAG, "Motion Cancel");
      xTouch = (int) event.getX(0);
      yTouch = (int) event.getY(0);
      // this.mOptionButton.setDiscretePosition(xTouch, yTouch,
      // mOptionAreaList);
      this.mSelected = false;
      invalidate();
      handled = true;
      break;

    default:
      break;
    }

    handled = mActiveRegion.contains((int) event.getX(0), (int) event.getY(0));

    return super.onTouchEvent(event) || handled;
  }

}
